import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Stack;

public class SemanticoCIL implements ISemantico {

	HashMap variaveis = new HashMap();
	Stack pilhaSe = new Stack();
	Stack pilhaWhile = new Stack();
	Stack pilhaFor = new Stack();
	Stack pilhaSwitch = new Stack();
	int contaSe = 0;
	int contaWhile = 0;
	int contaFor = 0;
	int contaSwitch = 0;
	String nomePrograma;
	StringBuffer sb = new StringBuffer();

	public void escreverArquivo() {
		try {
			System.out.print("Codigo intermediario...");
			BufferedWriter out = new BufferedWriter(new FileWriter(nomePrograma
					+ ".il"));
			out.write(sb.toString());
			out.close();
			System.out.println("OK");
		} catch (IOException e) {
			System.err.println("Erro ao tentar salvar em arquivo:"
					+ e.getMessage());
		}
	}
//parte que só faz pegar no windows por causa do .NET framework
	public void compilaIL() {
		try {
			System.out.print("Compilacao codigo IL...");
			Runtime
					.getRuntime()
					.exec(
							"/Users/Davi/Documents/workspace/ilasm.bat  /Users/Davi/Documents/workspace/"
									+ nomePrograma + ".il");
			System.out.println("OK");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void programaIni(String nomePrograma) {
		System.out.print("Parsing...");
		this.nomePrograma = nomePrograma;
		sb.append(".assembly " + nomePrograma + "{}\n");
		sb.append(".class " + nomePrograma);
		sb.append("{\n");
		sb.append("  .method static public void main() il managed\n");
		sb.append("  {\n");
		sb.append("    .entrypoint\n");
	}

	public void programaFim() {
		sb.append("    ret\n");
		sb.append("  }\n");
		sb.append("}\n");
		System.out.println("OK");
		escreverArquivo();
		compilaIL();
	}

	public void declaraVariavel(String str) {
		variaveis.put(str, new Integer(99));
		sb.append("    .locals(int32 " + str + ")\n");
	}

	public void atribuicao(String str, Object valor) {
		if (variaveis.containsKey(str)) {
			variaveis.put(str, valor);
			sb.append("\n");
			sb.append("    ldc.i4 "+(Integer)valor+"\n");
			sb.append("    stloc " + str + "\n");
		} else {
			System.err.println("erro: Variavel " + str + " nao declarada.");
			System.exit(1);
		}
	}

	public void atribuicao(String str) {
		if (variaveis.containsKey(str)) {
			sb.append("\n");
			sb.append("    stloc " + str + "\n");
		} else {
			System.err.println("erro: Variavel " + str + " nao declarada.");
			System.exit(1);
		}
	}

	public void impressaoVariavel(String str) {
		if (variaveis.containsKey(str)) {
			sb.append("\n");
			sb.append("    ldloc " + str + "\n");
			sb.append("    call void [mscorlib]System.Console::WriteLine(int32)\n");
		} else {
			System.err.println("erro: Variavel " + str + " nao declarada.");
			System.exit(1);
		}

	}

	public void leituraValor(String str) {
		sb.append("\n");
		sb.append("    ldstr \"Digite um numero inteiro: \"\n");
		sb.append("    call void [mscorlib]System.Console::Write(string)\n");
		sb.append("    call string [mscorlib]System.Console::ReadLine()\n");
		sb.append("    call int32 [mscorlib]System.Int32::Parse(string)\n");
		sb.append("    stloc " + str + "\n");
	}

	public void impressaoValor() {
		sb.append("\n");
		sb.append("    call void [mscorlib]System.Console::WriteLine(int32)\n");
	}

	public void condicaoInicio(Boolean b) {
		System.out.println("ENTROU AQUI2");
		contaSe++;
		pilhaSe.push(new Integer(contaSe));
		sb.append("\n");
		sb.append("    ldc.i4 " + (b.booleanValue() ? 1 : 0) + "\n");
		sb.append("    brfalse senaose" + contaSe + "\n");
	}
	
	public void condicaoEntao() {
		sb.append("    br _FIMIF\n");
		sb.append("    _ENTAO" + pilhaSe.peek() + ":\n");
	}

	public void condicaoSenao() {
		sb.append("    br _FIMIF\n");
	}
	
	public void condicaoFim() {
		contaSe = 0;
		pilhaSe.clear();
		sb.append("\n");
		sb.append("    _FIMIF:\n");
	}
	
	public void deInicio()
	{
		contaFor++;
		pilhaFor.push(new Integer(contaFor));
		sb.append("\n");
		sb.append("    ldstr \"Inicio FOR \"\n");
		sb.append("    call void [mscorlib]System.Console::WriteLine(string)\n");
		sb.append("    _FOR"+contaFor+":\n");
	}
	
	public void deFim()
	{
		sb.append("\n");
		sb.append("    _FIMFOR"+pilhaFor.pop()+":\n");
	}

	public void condicaoDe() {
		sb.append("    br _FOR"+pilhaFor.peek()+"\n");
	}

	public void desvioCaso()
	{
		sb.append("\n");
		sb.append("    _CASO"+pilhaSwitch.peek()+":\n");
	}

	public void desvioFim()
	{
		sb.append("\n");
		sb.append("    _FIMSWITCH:\n");
	}
	
	public void condicaoDesvio()
	{
		sb.append("    br _FIMSWITCH\n");
	}
	
	public void desvioInicio()
	{
		sb.append("\n");
		sb.append("    ldstr \"Inicio SWITCH CASE \"\n");
		sb.append("    call void [mscorlib]System.Console::WriteLine(string)\n");
	}
	
	public void enquantoInicio()
	{
		contaWhile++;
		pilhaWhile.push(new Integer(contaWhile));
		sb.append("\n");
		sb.append("    ldstr \"Inicio WHILE \"\n");
		sb.append("    call void [mscorlib]System.Console::WriteLine(string)\n");
		sb.append("    _WHILE"+contaWhile+":\n");
	}
	
	public void enquantoFim()
	{
		sb.append("\n");
		sb.append("    _FIMWHILE"+pilhaWhile.pop()+":\n");
	}

	public void condicaoEnquanto() {
		sb.append("    br _WHILE"+pilhaWhile.peek()+"\n");
	}
	
	public void expressaoLogica(Object a, Object b, String op, String tipo)
	{
		System.out.println("LOGICA");
		System.out.println(a+"  - "+op+ " - "+tipo);
		String flag = "";
		
		Object x = null;
		Object y = null;
		
		if(tipo == "tt")
		{
			x = (Integer) a;
			y = (Integer) b;			
		}
		else if(tipo == "ii")
		{
			x = (String) a;
			y = (String) b;
		}
		else if(tipo == "ti")
		{
			x = (Integer) a;
			y = (String) b;
		}
		else
		{
			x = (String) a;
			y = (Integer) b;
		}

		flag += (x instanceof Integer ? "    ldc.i4 ":"    ldloc ") +x+"\n";
		flag += (y instanceof Integer ? "    ldc.i4 ":"    ldloc ") +y+"\n";
		try
		{
			if(op == ">")
			{
				flag += "    blt";
			}
			else if(op == "<")
			{
				flag += "    bgt";
			}
			else if(op == ">=")
			{
				flag += "    ble";
			}
			else if(op == "<=")
			{
				flag += "    bge";
			}
			else if(op == "!=")
			{
				flag += "    beq";
			}
			else if(op == "==")
			{
				flag += "    ceq\n    brfalse";
			}
		}
		catch(Exception e)
		{
			System.out.println(e.getMessage());
		}
		sb.append(flag);
		System.out.println(flag);
		
		System.out.println("-----------------------------------");
		System.out.println(sb.toString());
	}
	
	public void branchIf()
	{
		contaSe++;
		pilhaSe.push(new Integer(contaSe));
		sb.append(" _ENTAO" + pilhaSe.peek() + "\n");
	}

	public void branchWhile()
	{
		sb.append(" _FIMWHILE"+pilhaWhile.peek()+"\n");
	}

	public void branchFor()
	{
		sb.append(" _FIMFOR"+pilhaFor.peek()+"\n");
	}
	
	public void branchSwitch()
	{
		contaSwitch++;
		pilhaSwitch.push(new Integer(contaSwitch));
		sb.append(" _CASO"+pilhaSwitch.peek()+"\n");
	}
	
	public void numero(Integer num) {
		sb.append("    ldc.i4 " + num + "\n");
	}

	public void operar(Object a, Object b,String op, String tipo) {
		String flag = "";
		
		Object x = null;
		Object y = null;
		
		if(tipo == "tt")
		{
			x = (Integer) a;
			y = (Integer) b;			
		}
		else if(tipo == "ii")
		{
			x = (String) a;
			y = (String) b;
		}
		else if(tipo == "ti")
		{
			x = (Integer) a;
			y = (String) b;
		}
		else
		{
			x = (String) a;
			y = (Integer) b;
		}

		flag += (x instanceof Integer ? "    ldc.i4 ":"    ldloc ") +x+"\n";
		flag += (y instanceof Integer ? "    ldc.i4 ":"    ldloc ") +y+"\n";
		sb.append(flag);
		
		if(op == "+")
		{
			sb.append("    add\n");
		}
		else if(op == "-")
		{
			sb.append("    sub\n");
		}
		else if(op == "*")
		{
			sb.append("    mul\n");
		}
		else if(op == "/")
		{
			sb.append("    div\n");
		}
	}
}
